#include "bfcp.h"
#include "zklog/zklog_wrapper.h"

using namespace resip;
using namespace bfcp;

//////////////////////////////////////////////////////////////////////////
BfcpParticipantKey::BfcpParticipantKey()
    : mConfId(0), mUserId(0)
{
}

BfcpParticipantKey::BfcpParticipantKey(UInt32 confId, UInt16 userId)
    : mConfId(confId), mUserId(userId)
{
}

BfcpParticipantKey::BfcpParticipantKey(const BfcpParticipantKey& rhs)
    : mConfId(rhs.mConfId), mUserId(rhs.mUserId)
{
}

BfcpParticipantKey BfcpParticipantKey::operator=(const BfcpParticipantKey& rhs)
{
    if (this != &rhs)
    {
        mConfId = rhs.mConfId;
        mUserId = rhs.mUserId;
    }

    return *this;
}

bool BfcpParticipantKey::operator<(const BfcpParticipantKey& rhs) const
{
    if (mUserId != rhs.mUserId)
    {
        return mUserId < rhs.mUserId;
    }
    if (mConfId != rhs.mConfId)
    {
        return mConfId < rhs.mUserId;
    }

    return false;
}

EncodeStream& operator<<(EncodeStream& strm, const BfcpParticipantKey& key)
{
    return strm << "[cid:" << key.mConfId << ", uid:" << key.mUserId << "]";
}


//////////////////////////////////////////////////////////////////////////
BFCPClient::BFCPClient(resip::Fifo<bfcp::BfcpTransactionMessage>& rxFifo, UInt32 confId, UInt16 userId)
    : mBFCPMessages(rxFifo, 8)
    , mKey(confId, userId)
    , mLastPingTime(0)
{
}

BFCPClient::BFCPClient(UInt32 confId, UInt16 userId, const resip::Data& printableAddress, int port, resip::TransportType type/* = resip::UDP*/)
    : mBFCPMessages(mFifo, 8)
    , mTarget(printableAddress, port, type)
    , mKey(confId, userId)
    , mLastPingTime(0)
{
}

BFCPClient::~BFCPClient()
{
    shutdown();
    join();
}

void BFCPClient::run()
{
    runInternal();
    ThreadIf::run();
}

void BFCPClient::shutdown()
{
    requestGoodbye();

    shutdownInternal();
    ThreadIf::shutdown();
}

void BFCPClient::thread()
{
    ZKLOG_INFO_FMT("BFCP Client running...");
    while (!isShutdown())
    {
        try
        {
            bfcp::BfcpTransactionMessage* msg(mStateMacFifo.getNext(1000));  // Only need to wake up to see if we are shutdown
            if (msg)
            {
                process(msg);
            }

            UInt64 now = resip::ResipClock::getTimeSecs();
            if (!mLastPingTime)
            {
                requestHello();
                mLastPingTime = now;
            }
            else
            {
                if (now - mLastPingTime > 10)
                {
                    requestHello();
                    mLastPingTime = now;
                }
            }
        }
        catch (std::exception& e)
        {
            ZKLOG_WARNING << "Unhandled exception: " << e.what();
        }
    }
    ZKLOG_INFO << "Shutting down bfcp server thread";
}

std::shared_ptr<resip::ConsumerFifoBuffer<bfcp::BfcpTransactionMessage> > BFCPClient::getConsumerFifo()
{
    return std::make_shared<resip::ConsumerFifoBuffer<bfcp::BfcpTransactionMessage> >(mFifo, 8);
}

std::auto_ptr<bfcp::BfcpMessage> BFCPClient::requestHello()
{
    return BfcpStack::requestHello(mTarget, mKey.getConferenceId(), mKey.getUserId());
}

std::auto_ptr<bfcp::BfcpMessage> BFCPClient::requestGoodbye()
{
    return BfcpStack::requestGoodbye(mTarget, mKey.getConferenceId(), mKey.getUserId());
}

std::auto_ptr<bfcp::BfcpMessage> BFCPClient::responseFloorStatusAck(const bfcp::BfcpMessage* req)
{
    return BfcpStack::responseFloorStatusAck(req);
}

std::auto_ptr<bfcp::BfcpMessage> BFCPClient::responseGoodbyeAck(const bfcp::BfcpMessage* req)
{
    return BfcpStack::responseGoodbyeAck(req);
}

void BFCPClient::processIncoming(bfcp::BfcpTransactionMessage* msg)
{
    //mBFCPMessages.add(msg);
    BfcpMessage* bfcpMsg = dynamic_cast<BfcpMessage*>(msg);
    if (!bfcpMsg) return;

    switch (bfcpMsg->getPrimitive())
    {
    case Hello:
        responseHelloAck(bfcpMsg);
        return;
    case FloorStatus:
        responseFloorStatusAck(bfcpMsg);
        return;
    case Goodbye:
        responseGoodbyeAck(bfcpMsg);
    default:
        break;
    }

    delete bfcpMsg;
}

